/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.inspur.edp.component.schema.utils;

import com.inspur.edp.caf.cef.schema.base.datatype.BasePrimitiveType;
import com.inspur.edp.caf.cef.schema.base.datatype.impl.DefaultCollectionType;
import com.inspur.edp.caf.cef.schema.base.datatype.impl.DefaultMapType;
import com.inspur.edp.caf.cef.schema.base.datatype.impl.DefaultPrimitiveType;
import com.inspur.edp.caf.cef.schema.datatype.DataType;
import com.inspur.edp.caf.cef.schema.datatype.ObjectType;
import com.inspur.edp.caf.cef.schema.datatype.PrimitiveTypeKind;
import com.inspur.edp.component.schema.GspCompParamType;
import com.inspur.edp.component.schema.StructuredTypeRefer;
import com.inspur.edp.component.schema.enums.CollectionType;
import com.inspur.edp.component.schema.enums.GspCompDataType;
import com.inspur.edp.component.schema.type.array.ArrayType;
import com.inspur.edp.component.schema.type.custom.CustomType;
import io.iec.edp.caf.commons.exception.CAFRuntimeException;
import io.iec.edp.caf.commons.exception.ExceptionLevel;

/**
 * @author Kaixuan Shi
 * @version 0.1
 */
public class DataTypeConverter {

    //region DataTypeTrans
    public static DataType getDefaultDataType(GspCompParamType paramType) {
        CollectionType collectionType = paramType.getCollectionType();
        if (collectionType == null) {
            throw new DataTypeTransferException("CollectionType cannot be null");
        }
        switch (collectionType) {
            case None:
                return getBaseType(paramType);
            case List:
                DefaultCollectionType colType = new DefaultCollectionType();
                DataType elementType = getDefaultDataType(paramType.getValueType());
                colType.setElementType(elementType);
                return colType;
            case Array:
                ArrayType arrayType = new ArrayType();
                DataType arrayElementType = getDefaultDataType(paramType.getValueType());
                arrayType.setElementType(arrayElementType);
                return arrayType;
            case Map:
                DefaultMapType mapType = new DefaultMapType();
                DataType keyType = getDefaultDataType(paramType.getKeyType());
                DataType valueType = getDefaultDataType(paramType.getValueType());
                mapType.setKeyType(keyType);
                mapType.setValueType(valueType);
                return mapType;
            default:
                throw new DataTypeTransferException("Unsupported CollectionType:" + collectionType);
        }
    }

    private static DataType getBaseType(GspCompParamType paramType) {
        GspCompDataType type = paramType.getType();
        if (type == null) {
            throw new DataTypeTransferException("the field \"type\" cannot be null when the CollectionType is \"None\"!");
        }
        switch (type) {
            case Custom:
                return new CustomType(paramType.getCustomPkgName(), paramType.getCustomClassName());
            case Complex:
                return paramType.getComplexType();
            default:
                return type.getType();
        }
    }
    //endregion

    public static GspCompParamType getGspCompParamType(DataType dataType) {
        GspCompParamType paramType = new GspCompParamType();
        if (dataType instanceof DefaultPrimitiveType) {
            paramType.setCollectionType(CollectionType.None);
            GspCompDataType type = getGspCompDataType(dataType);
            paramType.setType(type);
        } else if (dataType instanceof DefaultCollectionType) {
            DataType elementType = ((DefaultCollectionType) dataType).getElementType();
            GspCompParamType valueType = getGspCompParamType(elementType);

            paramType.setCollectionType(CollectionType.List);
            paramType.setValueType(valueType);
        } else if (dataType instanceof ArrayType) {
            DataType elementType = ((ArrayType) dataType).getElementType();
            GspCompParamType valueType = getGspCompParamType(elementType);

            paramType.setCollectionType(CollectionType.Array);
            paramType.setValueType(valueType);
        } else if (dataType instanceof DefaultMapType) {
            DataType keyTypeData = ((DefaultMapType) dataType).getKeyType();
            DataType valueTypeData = ((DefaultMapType) dataType).getValueType();
            GspCompParamType keyType = getGspCompParamType(keyTypeData);
            GspCompParamType valueType = getGspCompParamType(valueTypeData);

            paramType.setCollectionType(CollectionType.Map);
            paramType.setKeyType(keyType);
            paramType.setValueType(valueType);
        } else if (dataType instanceof ObjectType) {
            paramType.setCollectionType(CollectionType.None);
            paramType.setType(GspCompDataType.Object);
        } else if (dataType instanceof CustomType) {
            String className = ((CustomType) dataType).getClassName();
            String pkgName = ((CustomType) dataType).getPackageName();

            paramType.setCollectionType(CollectionType.None);
            paramType.setType(GspCompDataType.Custom);
            paramType.setCustomPkgName(pkgName);
            paramType.setCustomClassName(className);
        } else if (dataType instanceof StructuredTypeRefer) {
            paramType.setCollectionType(CollectionType.None);
            paramType.setType(GspCompDataType.Complex);
            paramType.setComplexType((StructuredTypeRefer) dataType);
        } else {
            throw new DataTypeTransferException("Unsupported DataType conversion:" + dataType.getKind());
        }

        return paramType;
    }

    public static GspCompDataType getGspCompDataType(DataType dataType) {
        if (dataType instanceof ObjectType) {
            return GspCompDataType.Object;
        } else if (dataType instanceof DefaultPrimitiveType) {
            PrimitiveTypeKind primitiveTypeKind = ((BasePrimitiveType) dataType).getPrimitiveTypeKind();
            switch (primitiveTypeKind) {
                case String:
                    return GspCompDataType.String;
                case Byte:
                    return GspCompDataType.Byte;
                case Int16:
                case Int32:
                case Int64:
                    return GspCompDataType.Int;
                case Bool:
                    return GspCompDataType.Bool;
                case Float:
                case Double:
                case Decimal:
                    return GspCompDataType.Decimal;
                case DateTime:
                    return GspCompDataType.Date;
                case TimeStamp:
                    return GspCompDataType.DateTime;
                default:
                    throw new DataTypeTransferException("Unsupported DataType conversion:" + primitiveTypeKind.name());
            }
        } else if (dataType instanceof CustomType) {
            return GspCompDataType.Custom;
        } else if (dataType instanceof StructuredTypeRefer) {
            return GspCompDataType.Complex;
        } else {
            throw new DataTypeTransferException("Unsupported DataType conversion:" + dataType.getKind());
        }
    }

    private static class DataTypeTransferException extends CAFRuntimeException {

        private static final long serialVersionUID = 2945828414166729158L;
        private static final String SERVICE_UNIT_CODE = "pfcommon";
        private static final String EXCEPTION_CODE = "GSP_COMPONENT_SCHEMA_0001";

        public DataTypeTransferException(String message) {
            super(SERVICE_UNIT_CODE, EXCEPTION_CODE, String.valueOf(message), null, ExceptionLevel.Error, false);
        }
    }
}
